/*-----------------------------------------------------------------------------
 * @file   ring_buf.h
 * @brief  ring buffer (thread safe), DO NOT support dynamic expanded memory
 * @author WLH<wanglehui8357@163.com> Copyright (C) 2021~2099.
 ----------------------------------------------------------------------------*/
#include "ring_buf.h"
#include "hal_irq.h"

/*-----------------------------------------------------------------------------
 * public functions
-----------------------------------------------------------------------------*/
ring_buf_t* ringbuf_create(U8* data_ptr, const U32 size)
{
    assert(NULL != data_ptr);
    assert(size > sizeof(ring_buf_t));
    if (NULL == data_ptr || size <= sizeof(ring_buf_t))
    {
        return NULL;
    }
    ring_buf_t* buffer = (ring_buf_t*)data_ptr;
    buffer->capacity = size - sizeof(ring_buf_t);
    buffer->data_ptr = &data_ptr[sizeof(ring_buf_t)];
    buffer->start = 0;
    buffer->end = 0;
    return buffer;
}

void ringbuf_clear(ring_buf_t* const buffer)
{
    assert(buffer != NULL);
    if (buffer != NULL)
    {
        CRITICAL_SECTION_ENTER
        buffer->start = 0;
        buffer->end = 0;
        CRITICAL_SECTION_LEAVE
    }
}

U32 ringbuf_used(ring_buf_t* const buffer)
{
    assert(buffer != NULL);
    U32 result = 0U;

    /* remove Warning[Pa082]:
     * undefined behavior: the order of volatile accesses is undefined */
    if (buffer != NULL)
    {
        CRITICAL_SECTION_ENTER
        result = buffer->end;
        result -= buffer->start;
        result += buffer->capacity;
        result %= buffer->capacity;
        CRITICAL_SECTION_LEAVE
    }

    return result;
}

U32 ringbuf_unused(ring_buf_t* const buffer)
{
    U32 result = 0;
    assert(buffer != NULL);
    /* remove Warning[Pa082]:
     * undefined behavior: the order of volatile accesses is undefined */
    if (buffer != NULL)
    {
        CRITICAL_SECTION_ENTER
        result = buffer->end;
        result -= buffer->start;
        result += buffer->capacity;
        result %= buffer->capacity;
        result = buffer->capacity - result;
        CRITICAL_SECTION_LEAVE
    }

    return result;
}

bool ringbuf_is_empty(ring_buf_t* const buffer)
{
    bool result = false;
    U32 temp;
    assert(buffer != NULL);

    /* remove Warning[Pa082]:
     * undefined behavior: the order of volatile accesses is undefined */
    if (buffer != NULL)
    {
        CRITICAL_SECTION_ENTER
        temp = buffer->end;
        result = temp == buffer->start;
        CRITICAL_SECTION_LEAVE
    }

    return result;
}

bool ringbuf_is_full(ring_buf_t* const buffer)
{
    bool result = false;
    U32 temp;
    assert(buffer != NULL);
    /* remove Warning[Pa082]:
     * undefined behavior: the order of volatile accesses is undefined */
    if (buffer != NULL)
    {
        CRITICAL_SECTION_ENTER
        temp = buffer->end + 1;
        temp %= buffer->capacity;
        result = buffer->start == temp;
        CRITICAL_SECTION_LEAVE
    }

    return result;
}

bool ringbuf_putc(ring_buf_t* buffer, const U8 data)
{
    assert(buffer != NULL);
    bool result = false;
    if (buffer != NULL && !ringbuf_is_full(buffer))
    {
        CRITICAL_SECTION_ENTER
        buffer->end = (buffer->end + 1) % buffer->capacity;
        buffer->data_ptr[buffer->end] = data;
        CRITICAL_SECTION_LEAVE
        result = true;
    }
    return result;
}

U8 ringbuf_getc(ring_buf_t* buffer)
{
    U8 data = 0U;
    assert(buffer != NULL);

    if (buffer != NULL && !ringbuf_is_empty(buffer))
    {
        CRITICAL_SECTION_ENTER
        buffer->start = (buffer->start + 1) % buffer->capacity;
        data = buffer->data_ptr[buffer->start];
        CRITICAL_SECTION_LEAVE
    }

    return data;
}

U32 ringbuf_write(ring_buf_t* buffer, const U8* data, U32 size)
{
    assert(buffer != NULL);
    assert(data != NULL);

    const U32 unused_sz = ringbuf_unused(buffer);
    /* discard overflow data */
    if (size >= unused_sz)
    {
        return 0u;
    }

    if (buffer != NULL && data != NULL && size > 0)
    {
        CRITICAL_SECTION_ENTER
        for (U32 i = 0; i < size; i++)
        {
            buffer->end = (buffer->end + 1) % buffer->capacity;
            buffer->data_ptr[buffer->end] = data[i];
        }
        CRITICAL_SECTION_LEAVE
    }

    return size;
}

U32 ringbuf_read(ring_buf_t* buffer, U8* target, U32 amount)
{
    assert(buffer != NULL);
    assert(target != NULL);
    assert(amount > 0);

    const U32 used_sz = ringbuf_used(buffer);
    if (amount > used_sz)
    {
        amount = used_sz;
    }

    if (buffer != NULL && target != NULL)
    {
        CRITICAL_SECTION_ENTER
        for (U16 i = 0; i < amount; i++)
        {
            buffer->start = (buffer->start + 1) % buffer->capacity;
            target[i] = buffer->data_ptr[buffer->start];
        }
        CRITICAL_SECTION_LEAVE
    }
    return amount;
}
